<template>
  <div ref="treeBox" class="treeBox" style="height: 100%">
    <template v-if="isOperator.length > 0">
      <div v-show="(hasPermission.delete || hasPermission.add) ? true : false" class="mb5">
        <el-button
            v-if="hasPermission.add"
            :icon="Plus"
            size="small"
            style="margin-left: 2px"
            type="primary"
            @click="addNode"
        >
          添加节点
        </el-button>

        <el-button
            v-if="hasPermission.delete"
            :icon="Delete"
            size="small"
            style="margin-left: 76px"
            type="danger"
            @click="removeNode"
        >
          删除节点
        </el-button>
      </div>

      <el-input
          v-if="hasPermission.search"
          ref="searchInputRef"
          v-model="searchQuery"
          :maxlength="20"
          class="mb5"
          clearable
          placeholder="输入关键字搜索"
          show-word-limit
          size="small"
      ></el-input>
    </template>

    <el-tree
        ref="treeRef"
        :allow-drag="allowDrag"
        :allow-drop="allowDrop"
        :check-on-click-node="checkOnClickNode"
        :check-strictly="checkStrictly"
        :data="treeData"
        :default-checked-keys="defaultKeys"
        :default-expand-all="defaultExpandAll"
        :draggable="isDraggable"
        :filter-node-method="filterNode"
        :indent="0"
        :node-key="nodeKey"
        :props="defaultProps"
        :show-checkbox="isShowCheckbox"
        :style="{
          width: width? width + 'px' : '100%',
          height: `calc(100% - ${marginBottom}px - ${isOperator.length > 0 ? 40 : 0}px)`,
       }"
        class="tree"
        v-bind="$attrs"
        @node-drag-end="handleDragEnd"
        @node-click="handleNodeClick"
        @node-contextmenu="editNode"
        @check-change="getCheckedAllNodes"
    >
      <template #default="{ node }">
        <div :style="nodeContentStyle" class="node-content">
          <!--                    <SvgIcon :name="checkIconByNodeLevel(node)" class="mr5"></SvgIcon>-->
          <span v-if="isShowIcon" style="display: flex;
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */">
            <el-icon :size="16" class="mr5" v-if="node.data[props.defaultProps.alarm]">
              <!--            // 活动告警-->
              <AlarmIcon/>
            </el-icon>
            <el-icon :size="16" class="mr5" v-if="node.data[props.defaultProps.isTask] === true">
              <!--            子节点//活动节点-->
              <ActivitiesIcon/>
            </el-icon>
            <el-icon :size="16" class="mr5" v-else>
              <!--            父节点-->
              <PIcon/>
            </el-icon>

          </span>

          <input
              v-if="showIpt && node.data[props.defaultProps.label] === curNodLabel && isEditNode"
              ref="inputRef"
              :value="node.data[props.defaultProps.label]"
              type="text"
              @blur="showIpt = false"
              @keyup.enter="updateNodeLabel($event, node)"
          />
          <el-tooltip
              :content="node.data[props.defaultProps.label]"
              :disabled="showTitle"
              effect="dark"
              placement="right"
          >
						<span
                :class="compNodeCustomized(node)"
                class="ellipsis"
                @mouseover="onShowNameTipsMouseenter"
            >
							<span class="node-txt">{{ node.data[props.defaultProps.label] }}</span>
              <template v-if="showMore">
                <span class="nodeCount" v-show="emptyJudgment(node.data[props.defaultProps.childCount])">({{
                    node.data[props.defaultProps.childCount]
                  }})</span>
               <el-tag class="tagCustom"
                       v-show="emptyJudgment(node.data[props.defaultProps.status])"
                       :class="[customTagClassOrContent(node.data[props.defaultProps.status])]">{{
                   customTagClassOrContent(node.data[props.defaultProps.status], '')
                 }}</el-tag>

              <span class="customStatus running"
                    :class="[customStatus(node.data[props.defaultProps.running], 'class')]"
                    v-show="emptyJudgment(node.data[props.defaultProps.running])"><b
                  class="txt">●</b> {{ customStatus(node.data[props.defaultProps.running]) }}</span>
              <span class="customStatus"
                    v-show="emptyJudgment(node.data[props.defaultProps.personnel])">{{
                  node.data[props.defaultProps.personnel]
                }}</span>
              <span class="customStatus"
                    v-show="emptyJudgment(node.data[props.defaultProps.completionRate])">完成度：{{
                  node.data[props.defaultProps.completionRate]
                }}</span>
              <span class="customStatus"
                    v-show="emptyJudgment(node.data[props.defaultProps.estimatedWorkingHours])">预计工时：{{
                  node.data[props.defaultProps.estimatedWorkingHours]
                }}</span>
              <span class="customStatus"
                    v-show="emptyJudgment(node.data[props.defaultProps.cumulativeWorkingHours])">累计工时：{{
                  node.data[props.defaultProps.cumulativeWorkingHours]
                }}</span>
              <span class="customStatus"
                    v-show="emptyJudgment(node.data[props.defaultProps.pointsAccumulation])">积分已累计：{{
                  node.data[props.defaultProps.pointsAccumulation]
                }}</span>
              </template>

						</span>
          </el-tooltip>
        </div>
      </template>
    </el-tree>
    <RightClickMenu
        v-if="isRightClickMenu"
        :menuItems="menuItems"
        :menuLeft="menuLeft"
        :menuTop="menuTop"
        :visible="menuVisible"
        @click="handleMenuEdit"
    />
  </div>
</template>

<script lang="ts" setup>
import {nextTick, ref, computed, watch, onMounted, onBeforeUnmount} from 'vue';
import type Node from 'element-plus/es/components/tree/src/model/node';
import {Plus, Delete} from '@element-plus/icons-vue'
import PIcon from './pIcon.vue'
import ActivitiesIcon from './activitiesIcon.vue'
import AlarmIcon from './alarmIcon.vue'

import RightClickMenu from './rightClickMenu.vue'

import {MenuItem} from "@/views/components/tree/type.ts";
import {Tree} from "element-plus/es/components/tree-v2/src/types";

const treeBox = ref(null)
const showIpt = ref<boolean>(false); // 是否显示输入框
const curNodLabel = ref<string>(); // 记录右键点击的节点
const inputRef = ref(); // 输入框实例
const treeRef = ref(); // 树实例
const searchInputRef = ref(); // 树搜索实例
//判断是否显示tooltip
const showTitle = ref<boolean>(true);

const menuVisible = ref(false);
const menuTop = ref(0);
const menuLeft = ref(0);

function onShowNameTipsMouseenter(e: MouseEvent) {
  /**
   * onShowNameTipsMouseenter 函数被用作鼠标悬停，因此它应该接收一个 MouseEvent 类型的参数
   * 访问一个 EventTarget 对象的属性如 clientWidth 和 scrollWidth 时，你会遇到类型错误。
   * 因为 EventTarget 类型并不具有这些属性。这些属性是 HTMLElement 类型的一部分。
   * 因此，你需要将 e.target 断言为 HTMLElement。
   */
  const target = e.target as HTMLElement; //减少属性访问次数、提高性能
  if (!target) return;
  let textLength = target?.clientWidth; // 获取文本宽度
  let containerLength = target.scrollWidth; // 获取容器宽度
  // 如果文本宽度小于容器宽度，没溢出、不显示提示
  if (textLength < containerLength) {
    showTitle.value = false;
  } else showTitle.value = true;
}

// 判断节点能否被放置 如果返回 false ，节点不能被放置
const allowDrop = () => true;
// 判断节点能否被拖拽 如果返回 false ，节点不能被拖动
const allowDrag = () => true;
// 搜索
const searchQuery = ref('');

/* props
---------------------------- Satrt */
interface DefaultProps {
  id: string;
  children: string;
  label: string;
  disabled: string;
  childCount: string;
  status: string;
  personnel: string;
  completionRate: string;
  estimatedWorkingHours: string;
  cumulativeWorkingHours: string;
  pointsAccumulation: string;
  running: string;
  class: string;
  alarm: string;
  isTask: string;
}

const emits = defineEmits(['eCurNode', 'eCheckedNodes', 'eSaveNodes', 'eRightClick', "onRightClick"]);

interface Props {
  width?: Number,
  marginBottom?: Number,
  treeData?: any[]; // 树数据
  nodeKey?: number | string; // 节点唯一标识 -用于默认选中和展开
  checkStrictly?: boolean; // 是否严格模式 （true子父不关联 false子父关联）
  isShowCheckbox?: boolean; // 是否显示复选框
  checkOnClickNode?: boolean; // 点击节点时是否选中复选框
  defaultExpandAll?: boolean; // 是否默认展开所有
  isDraggable?: boolean; // 是否可拖拽
  isOperator?: string[]; // 是否有操作
  defaultKeys?: string[]; // 默认选中
  isEditNode?: boolean; // 是否可编辑节点
  isRightClickMenu?: boolean; // 是否可编辑节点
  ellipsisLen?: number; // 文本数量显示tooltip提示 - （没有用到暂时不删父组件可能有传递）
  defaultProps?: DefaultProps; // 默认属性
  treeIcon?: string[]; // 树图标 [0]:最后一级图标;  [1]:展开图标;  [2]:折叠图标;
  /*
   * 只有一级节点让其靠左对齐
   * 例如
   *	:node-content-style="{
   *			'margin-left': '-27px',
   *		}"
   * 如果有复选框可以在父组件加上以下样式控制复选框位置
   * :deep(.el-checkbox__inner) {
   *		margin-left: -30px;
   * }
   */
  showMore?: Boolean;
  isShowIcon?: Boolean;
  nodeContentStyle?: any;
  menuItems?: Array<MenuItem>
}

const props = withDefaults(defineProps<Props>(), {
  width: 0,
  marginBottom: 30,
  treeData: () => [],
  nodeKey: 'id',
  checkStrictly: false,
  isShowCheckbox: true,
  checkOnClickNode: false, // true:不关联，false:关联 （line:105 handleNodeClick做了处理）
  defaultExpandAll: true,
  isDraggable: false,
  isOperator: () => [],
  defaultKeys: () => [],
  isEditNode: false,
  ellipsisLen: 15,
  defaultProps: (): DefaultProps => ({
    id: 'strategyId',
    children: 'children',
    label: 'label',
    disabled: 'disabled',
    childCount: 'childCount',
    status: 'status',
    personnel: 'personnel',
    completionRate: 'completionRate',
    estimatedWorkingHours: 'estimatedWorkingHours',
    cumulativeWorkingHours: 'cumulativeWorkingHours',
    pointsAccumulation: 'pointsAccumulation',
    running: 'running',
    class: '',
    alarm: 'alarm',
    isTask: 'isTask',
  }),
  isRightClickMenu: false,
  treeIcon: () => ['FileOutlined', 'FolderOpenOutlined', 'MinusSquareOutlined'],
  menuItems: () => {
    return [
      {key: 'add', label: '添加', event: 'add'},
      {key: 'edit', label: '编辑', event: 'edit'},
    ]
  },
  showMore: () => {
    return false
  },
  isShowIcon: () => {
    return true
  }
});

/* props
---------------------------- End */

// 计算属性，用于判断操作权限
const hasPermission = computed(() => ({
  add: props.isOperator.includes('add'),
  delete: props.isOperator.includes('delete'),
  search: props.isOperator.includes('search'),
}));
// 点击节点时触发
const handleNodeClick = (data: Tree, node: { data: { disabled: boolean }; checked: boolean }) => {
  emits('eCurNode', {
    data,
    node
  });
  // 点击该行 如果不是复选框或是禁用的，不勾选
  if (!props.isShowCheckbox || node.data.disabled) return;
  // node.checked = !node.checked;
};

// 删除节点
const removeNode = () => {
  const checkedNodes = treeRef.value.getCheckedNodes();
  if (checkedNodes.length === 0) return alert('请至少勾选一项才能删除节点');
  for (const node of checkedNodes) {
    nextTick(() => {
      // id如果重复则会删除失败错乱
      treeRef.value.remove(node);
    });
  }
};

// 右击节点时触发
const editNode = (event: MouseEvent, node: Node & DefaultProps) => {
  // console.log(treeBox.value)
  emits('onRightClick', {event, node})
  event.preventDefault();
  if (!treeBox.value || !props.isRightClickMenu) {
    return false
  }
  let eleBox = treeBox.value.getBoundingClientRect();
  const offsetX = eleBox.left + window.pageXOffset;
  const offsetY = eleBox.top + window.pageYOffset;

  let margin = 10

  menuTop.value = event.clientY - offsetY + margin;
  menuLeft.value = event.clientX - offsetX + margin;

  menuVisible.value = true;
  curNodLabel.value = node;
  showIpt.value = true;
  nextTick(() => {
    inputRef.value?.focus();
  });
};

const handleMenuEdit = (str) => {
  // console.log('编辑节点：', selectedNode.value?.data);
  // console.log(str)
  emits('eRightClick', {
    type: str,
    node: curNodLabel.value
  })
  menuVisible.value = false;
};

// const handleMenuDelete = () => {
//   if (selectedNode.value) {
//     const {data, parent} = selectedNode.value;
//     if (parent) {
//       const children = (parent.data.children || parent.childNodes) as ElTree.Node[];
//       const index = children.findIndex(item => item.id === data.id);
//       if (index !== -1) {
//         children.splice(index, 1);
//         treeRef.value?.store.update(parent, 'childNodes', children);
//       }
//     }
//   }
//   menuVisible.value = false;
// };

const handleOutsideClick = (event) => {
  // 获取 el-tree 和 context-menu 的 DOM 元素
  const treeEl = document.querySelector('.el-tree');
  const contextMenuEl = document.querySelector('.right-click-menu');

  // console.log(treeEl, contextMenuEl)
  // 检查点击事件是否发生在 el-tree 或 context-menu 之外
  if (
      treeEl &&
      contextMenuEl &&
      !treeEl.contains(event.target as Node) &&
      !contextMenuEl.contains(event.target as Node)
  ) {
    // 隐藏上下文菜单（例如，通过修改 Vue 组件的状态）
    menuVisible.value = false;
  }
};

onMounted(() => {
  document.addEventListener('click', handleOutsideClick);
});

onBeforeUnmount(() => {
  document.removeEventListener('click', handleOutsideClick);
});

// 更新节点的label
const updateNodeLabel = (e: Event, node: Tree) => {
  const target = e.target as HTMLInputElement;
  if (isValueInTree(props.treeData, target.value)) return alert('该节点已存在');
  node = Object.assign({}, node);
  node.data[props.defaultProps.label] = target.value;
  showIpt.value = false;
};

// 使用非递归方式查找树中是否存在某个值
function isValueInTree(data: string | any[], value: string) {
  const stack = [...data];
  while (stack.length) {
    const node = stack.pop();
    if (node.data[props.defaultProps.label] === value) return true;
    if (Array.isArray(node.children)) {
      stack.push(...node.children);
    }
  }
  return false;
}

// 新增节点
const addNode = () => {
  const checkedNodes = treeRef.value.getCheckedNodes();
  if (checkedNodes.length === 0) return alert('请至少勾选一项才能添加节点');
  const nodeName = prompt('请输入节点名称');
  if (!nodeName) return;

  if (isValueInTree(props.treeData, nodeName)) return alert('该节点已存在');
  for (const parentNode of checkedNodes) {
    const newNode = {
      id: props.treeData.length + 1,
      label: nodeName,
    };
    if (!parentNode.children) {
      parentNode.children = [];
    }
    parentNode.children.push(newNode);
  }
};

// 结束拖拽
const handleDragEnd = (dropNode: Node) => {
  if (!dropNode) return;
  if (props.isDraggable === false) return;
  saveNode();
};

// 保存节点
function saveNode() {
  emits('eSaveNodes', props.treeData);
}

// 复选框改变
const getCheckedAllNodes = (data: Tree, isSelected: boolean) => {
  if (!props.isShowCheckbox) return;
  const checkedNodes = treeRef.value.getCheckedNodes();
  const halfCheckedNodes = treeRef.value.getHalfCheckedNodes();
  // data: 当前节点的数据
  // isSelected: 当前节点是否被选中
  // checkedNodes: 所有选中的节点
  // halfCheckedNodes: 所有半选中的节点
  emits('eCheckedNodes', data, isSelected, checkedNodes, halfCheckedNodes);
};

// 根据节点层级显示不同的图标
const checkIconByNodeLevel = computed(() => {
  return (node: { childNodes: []; expanded: boolean }) => {
    if (node.childNodes.length === 0) return `ant-${props.treeIcon[0]}`;
    return node.expanded ? `ant-${props.treeIcon[1]}` : `ant-${props.treeIcon[2]}`;
  };
});

// 根据id || disabled 控制节点颜色变化
const compNodeCustomized = computed(() => {
  // const customizedIds = [1]; // 假设dome指定需要自定义的节点id数组

  return (node: { data: { id: number; disabled: boolean } }) => {
    // 如果有禁用的节点，则加上样式
    if (node.data.disabled) return 'node-customized';
    // 如果节点id在指定的id数组中，则加上样式
    // return customizedIds.includes(node.data.id) ? 'node-customized' : '';
  };
});

// 判空
const emptyJudgment = (val) => {
  if (typeof val === 'number') {
    return true
  }
  return val && val !== '';
}

const customStatus = (status, type = '') => {
  status = String(status)
  if (type === 'class') {
    switch (status) {
      case '0':
        return 'info'
      case '1':
        return 'running';
      case '2':
        return 'close';
      case '3':
        return 'success';
      default:
        return 'info';
    }
  } else {
    switch (status) {
      case '0':
        return '未开始'
      case '1':
        return '进行中';
      case '2':
        return '已完成';
      case '3':
        return '已关闭';
      default:
        return '未开始';
    }
  }

}

const customTagClassOrContent = (status: string, type = 'class') => {
  status = String(status)
  if (type === 'class') {
    switch (status) {
      case '0':
        return 'tagInfo'
      case '1':
        return 'tagSuccess';
      case '2':
        return 'tagDanger';
      case '3':
        return 'tagPrimary';
      default:
        return 'tagInfo';
    }
  } else {
    switch (status) {
      case '0':
        return '待分解'
      case '1':
        return '已分解';
      case '2':
        return '有遗漏';
      case '3':
        return '超预期';
      default:
        return '待分解';
    }
  }
}

// 数据搜索
const filterNode = (value: string, data: any) => {
  if (!value) return true;
  return data[props.defaultProps.label].includes(value);
};
watch(searchQuery, (newVal) => {
  treeRef.value?.filter(newVal);
});

// onMounted(() => setTimeout(() => searchInputRef.value?.focus(), 1000));
defineExpose({
  treeRef,
  removeNode,
  addNode,
});
</script>

<style lang="scss" scoped>
@import "@/assets/var";

.treeBox {
  position: relative;
}

.mb5 {
  padding-left: 15px;
  padding-right: 15px;
  height: 32px;
  line-height: 32px;
}

.tree {
  margin: 0 10px;
  border-radius: 5px;
  // background-color: #d30808;
  padding: 15px;
  padding-bottom: 10px;
  overflow: auto;
  //overflow-x: hidden;
  //overflow-y: auto !important;
  //border-radius: var(--el-card-border-radius);
  //border: 1px solid var(--el-border-color-light);
  //background-color: #fff;
  //overflow: hidden;
  //color: var(--el-text-color-primary);
  //transition: var(--el-transition-duration);
  //border-radius: 4px;
  &:hover {
    box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
  }

  transition: box-shadow 0.3s ease-in-out;

  :deep(.el-tree-node) {
    position: relative;
  }

  :deep(.el-tree-node__children) {
    padding-left: 16px;
  }

  //:deep(.el-tree-node::before) {
  //  content: '';
  //  width: 22px;
  //  height: 20px;
  //  position: absolute;
  //  left: -12px;
  //  top: 1px;
  //  border-width: 1px;
  //  border-left: 1px dashed #ccc;
  //}

  //:deep(.el-tree-node:last-child::before) {
  //  height: 38px;
  //}

  //:deep(.el-tree-node::after) {
  //  content: '';
  //  width: 22px;
  //  height: 20px;
  //  position: absolute;
  //  left: -3px;
  //  top: 11px;
  //  border-width: 1px;
  //  border-top: 1px dashed #ccc;
  //}

  //& > :deep(.el-tree-node::after) {
  //  border-top: none;
  //}
  //
  //& > :deep(.el-tree-node::before) {
  //  border-left: none;
  //}
  //
  //:deep(.el-tree-node__expand-icon) {
  //  font-size: 16px;
  //
  //  :deep(&.is-leaf) {
  //    color: transparent;
  //    display: none;
  //  }
  //}
  //
  //.node-customized {
  //  color: #c4b6b3;
  //}

  /* 	一级不显示复选框其余内层都显示
  -----------------------------------------------------------*/
  // :deep(.el-checkbox .el-checkbox__inner) {
  // 	display: none;
  // }

  // :deep(div[role='group']) {
  // 	.el-checkbox .el-checkbox__inner {
  // 		display: inline-block;
  // 	}
  // }

  :deep(.el-tree-node__content) {
    height: 40px;
    line-height: 40px;
    font-size: 14px;
    color: #222222;
    //& .el-checkbox {
    // .el-checkbox__inner{
    //   display: none;
    // }
    //}
    .mr5 {
      margin-right: 5px;
    }

    &:hover {
      color: $themeColor;
      background: #edf1fc;
    }
  }

  :deep(.active ) {
    //:deep(.el-tree-node__content) {
    .el-tree-node__content{
      color: $themeColor;

      background: #edf1fc;
    }

    .el-tree-node__children{
      .el-tree-node__content{
        color: $--color-text-222;
        background: transparent;

      }
    }
    //}
  }

  //:deep(.is-current .el-tree-node__content) {
  //  //:deep(.el-tree-node__content) {
  //  color: $themeColor;
  //
  //  background: #edf1fc;
  //  //}
  //}

  /* 只有最后一级显示复选框
  -----------------------------------------------------------*/
  // :deep(.el-tree-node) {
  // 	.is-leaf + .el-checkbox .el-checkbox__inner {
  // 		display: inline-block;
  // 	}
  // 	.el-checkbox .el-checkbox__inner {
  // 		display: none;
  // 	}
  // }

  /* 隐藏所有复选框
  -----------------------------------------------------------*/
  // :deep(.el-checkbox .el-checkbox__inner) {
  // 	display: none;
  // }

  /* 只在三级菜单中显示复选框
  -----------------------------------------------------------*/
  // :deep(div[role='group'] div[role='group'] .el-checkbox .el-checkbox__inner) {
  // 	display: inline-block;
  // }
}

:deep(.el-input__wrapper, .el-input__wrapper.is-focus) {
  width: 100%;
}

.node-content {
  display: flex;
  align-items: center;
  width: 100%; /* 确保容器宽度填满 */
  min-width: 0; /* 防止flex子项溢出不换行 */
}

.ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex-basis: 1; /* 允许文本元素填充剩余空间 */
  max-width: calc(100% - 30px); /* 减去图标等其他元素占用的宽度 */
  .node-txt, .nodeCount {
    margin-right: 6px;
  }
}

//"primary" | "success" | "info" | "warning" | "danger"
.tagCustom {
  padding: 0 6px;
  margin: 0 18px;
}

.tagInfo {
  color: #222222;
  background: #e8e8e8;
  border-radius: 12px;
  border: 1px solid #e8e8e8;
}

.tagSuccess {
  color: #05c432;
  background: #baf4c0;
  border-radius: 12px;
  border: 1px solid #baf4c0;
}

.tagDanger {
  color: #f31010;
  background: #fdb0b0;
  border-radius: 12px;
  border: 1px solid #fdb0b0;
}

.tagPrimary {
  color: #00bccd;
  background: #b5edf2;
  border-radius: 12px;
  border: 1px solid #b5edf2;
}

.customStatus {
  margin-right: 30px;
}

.running {
  color: $themeColor;

  &.info {
    color: #787878;

    & .txt {
      color: #787878;
      font-size: 12px;
    }
  }

  &.success {
    color: #05c432;

    & .txt {
      color: #05c432;
      font-size: 12px;
    }
  }

  &.close {
    color: #f31010;

    & .txt {
      color: #f31010;
      font-size: 12px;
    }
  }

  & .txt {
    color: #5091fb;
    font-size: 12px;
  }
}

</style>
